﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Linq.Expressions;

namespace System.Web.Mvc.Html
{
    /// <summary>
    /// JQuery UI DatePicker helper.
    /// </summary>
    public static class rusisDatePickerHelper
    {
        /// <summary>
        /// Converts the .net supported date format current culture format into JQuery Datepicker format.
        /// </summary>
        /// <param name="html">HtmlHelper object.</param>
        /// <returns>Format string that supported in JQuery Datepicker.</returns>
        public static string ConvertDateFormat(this HtmlHelper html)
        {
            return ConvertDateFormat(html, Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern);
        }

        /// <summary>
        /// Converts the .net supported date format current culture format into JQuery Datepicker format.
        /// </summary>
        /// <param name="html">HtmlHelper object.</param>
        /// <param name="format">Date format supported by .NET.</param>
        /// <returns>Format string that supported in JQuery Datepicker.</returns>
        public static string ConvertDateFormat(this HtmlHelper html, string format)
        {
            /*
             *  Date used in this comment : 5th - Nov - 2009 (Thursday)
             *
             *  .NET    JQueryUI        Output      Comment
             *  --------------------------------------------------------------
             *  d       d               5           day of month(No leading zero)
             *  dd      dd              05          day of month(two digit)
             *  ddd     D               Thu         day short name
             *  dddd    DD              Thursday    day long name
             *  M       m               11          month of year(No leading zero)
             *  MM      mm              11          month of year(two digit)
             *  MMM     M               Nov         month name short
             *  MMMM    MM              November    month name long.
             *  yy      y               09          Year(two digit)
             *  yyyy    yy              2009        Year(four digit)             *
             */

            string currentFormat = format;

            // Convert the date
            currentFormat = currentFormat.Replace("dddd", "DD");
            currentFormat = currentFormat.Replace("ddd", "D");

            // Convert month
            if (currentFormat.Contains("MMMM"))
            {
                currentFormat = currentFormat.Replace("MMMM", "MM");
            }
            else if (currentFormat.Contains("MMM"))
            {
                currentFormat = currentFormat.Replace("MMM", "M");
            }
            else if (currentFormat.Contains("MM"))
            {
                currentFormat = currentFormat.Replace("MM", "mm");
            }
            else
            {
                currentFormat = currentFormat.Replace("M", "m");
            }

            // Convert year
            currentFormat = currentFormat.Contains("yyyy") ? currentFormat.Replace("yyyy", "yy") : currentFormat.Replace("yy", "y");

            return currentFormat;
        }


        public static HtmlString rusisDatePicker(this HtmlHelper html,
                        string name,
                        DateTime? valueStart = null,
                        DateTime? valueEnd = null,
                        DateTimeFormatInfo dateTimeFormat = null,
                        IDictionary<string, object> properties = null)
        {
            CultureInfo cultureInfo = new CultureInfo("ru-RU");
            //string dateFormatString = (dateTimeFormat ?? Thread.CurrentThread.CurrentCulture.DateTimeFormat).ShortDatePattern;
            string dateFormatString = (dateTimeFormat ?? cultureInfo.DateTimeFormat).ShortDatePattern;
            string dateFormatStringJQ = rusisDatePickerHelper.ConvertDateFormat(html, dateFormatString);

            var urlHelper = new UrlHelper(html.ViewContext.Controller.ControllerContext.RequestContext);

            string value = GetDateTimeRangeString(valueStart, valueEnd, dateFormatString);

            StringBuilder builder = new StringBuilder();

            builder.AppendLine("<script type='text/javascript'> ");

            if (properties.ContainsKey("enableRange") && properties["enableRange"].ToString().ToLower().Contains("true"))
            {
                builder.AppendLine("/* Singleton для работы с интервалами дат */ ");
                builder.AppendLine("var datepickerRange = { ");
                builder.AppendLine("    startDate: null, ");
                builder.AppendLine("    endDate: null, ");
                builder.AppendLine("    currentDate: new Date(), ");
                builder.AppendLine("    selectCount: 0, ");
                builder.AppendLine("    checkDays: function () { ");
                builder.AppendLine("        var self = this; ");
                builder.AppendLine("        if (this.startDate && this.endDate) { ");
                builder.AppendLine("            $(\"#" + name + "\").oneTime(1, function () { ");
                builder.AppendLine("                $(\"td>a.ui-state-default\").each(function (i) { ");
                builder.AppendLine("                    self.checkDay(this, i); ");
                builder.AppendLine("                }); ");
                builder.AppendLine("            }); ");
                builder.AppendLine("        } ");
                builder.AppendLine("    }, ");
                builder.AppendLine("    checkDay: function (elem, currentPos) { ");
                builder.AppendLine("        var currentDay = currentPos + 1; ");
                builder.AppendLine("        var currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth(), currentDay); ");
                builder.AppendLine("        if (currentDate.getTime() >= this.startDate.getTime() && currentDate.getTime() <= this.endDate.getTime()) { ");
                builder.AppendLine("            $(elem).addClass('ui-state-active').removeClass('ui-state-highlight'); ");
                builder.AppendLine("        } ");
                builder.AppendLine("    }, ");
                builder.AppendLine("    getSelectedDate: function (inst) { ");
                builder.AppendLine("        return new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay); ");
                builder.AppendLine("    } ");
                builder.AppendLine("}; ");
            }

            builder.AppendLine("$(function () { ");


            builder.AppendLine("   $('#" + name + "').datepicker({ ");
            if (properties.ContainsKey("buttonImageOnly") && properties["buttonImageOnly"].ToString().ToLower().Contains("true"))
            {
                builder.AppendLine("      buttonImage: '" + urlHelper.Content("~/Content/Images/calendar.gif") + "', ");
                builder.AppendLine("      buttonImageOnly: true, ");
                builder.AppendLine("      showOn: 'button', ");
            }
            builder.AppendLine("      numberOfMonths: 1, ");
            builder.AppendLine("      closeOnSelect: false, ");
            builder.AppendLine("      dateFormat: '" + dateFormatStringJQ + "' ");
            if (properties.ContainsKey("enableRange") && properties["enableRange"].ToString().ToLower().Contains("true"))
            {
                builder.AppendLine("      , ");
                builder.AppendLine("      \"onSelect\": function (dateText, inst) { ");
                builder.AppendLine("          datepickerRange.selectCount++; ");
                builder.AppendLine("          datepickerRange.currentDate = datepickerRange.getSelectedDate(inst); ");
                builder.AppendLine("          if (datepickerRange.selectCount < 2) { ");
                builder.AppendLine("              $(this).data().datepicker.inline = true; "); // Свойство для оставления открытым календарь
                builder.AppendLine("              datepickerRange.startDate = datepickerRange.getSelectedDate(inst); ");
                builder.AppendLine("              datepickerRange.endDate = null; ");
                builder.AppendLine("              $('#" + name + "').val(dateText); ");
                builder.AppendLine("          } else { ");
                builder.AppendLine("              $(this).data().datepicker.inline = false; "); // Свойство для оставления открытым календарь
                builder.AppendLine("              datepickerRange.selectCount = 0; ");
                builder.AppendLine("              datepickerRange.endDate = datepickerRange.getSelectedDate(inst); ");
                builder.AppendLine("              if (datepickerRange.startDate.getTime() > datepickerRange.endDate.getTime()) { ");
                builder.AppendLine("                  datepickerRange.endDate = datepickerRange.startDate; ");
                builder.AppendLine("                  datepickerRange.startDate = datepickerRange.currentDate; ");
                builder.AppendLine("              } else { ");
                builder.AppendLine("              } ");
                builder.AppendLine("                  $('#" + name + "').val(datepickerRange.startDate.format('" + dateFormatString.ToLower() + "') + ' - ' + datepickerRange.endDate.format('" + dateFormatString.ToLower() + "'));");
                builder.AppendLine("              datepickerRange.checkDays(); ");
                builder.AppendLine("          } ");
                builder.AppendLine("      }, ");
                builder.AppendLine("      \"onClose\": function () { ");
                builder.AppendLine("          $(this).data().datepicker.inline = false; ");
                builder.AppendLine("      }, ");
                builder.AppendLine("      \"beforeShow\": function () { ");
                builder.AppendLine("          datepickerRange.checkDays(); ");
                //builder.AppendLine("          if (datepickerRange.startDate != null && datepickerRange.endDate != null) { ");
                //builder.AppendLine("              $('#" + name + "').datepicker('setDate', datepickerRange.startDate); ");
                //builder.AppendLine("              $('#" + name + "').datepicker('setDate', datepickerRange.endDate); ");
                //builder.AppendLine("              $('#" + name + "').val( $('#" + name + "').val() ); ");
                //builder.AppendLine("          } ");
                builder.AppendLine("      }, ");
                builder.AppendLine("      \"onChangeMonthYear\": function (year, month, inst) { ");
                builder.AppendLine("          datepickerRange.currentDate = datepickerRange.getSelectedDate(inst); ");
                builder.AppendLine("          datepickerRange.checkDays(); ");
                builder.AppendLine("      } ");
            }
            builder.AppendLine("     }); ");
            builder.AppendLine("}); ");
            builder.AppendLine(" </script> ");

            string input = "<input type='text' id='" + name + "' name='" + name + "' " +
                ((!string.IsNullOrEmpty(value)) ? (" value = '" + value + "'") : "") +
                " />";
            builder.AppendLine(input);

            return new HtmlString(builder.ToString());
        }

        private static string GetDateTimeRangeString(DateTime? datetimeStart, DateTime? datetimeEnd, string dateFormatString)
        {
            if (datetimeStart.HasValue && datetimeEnd.HasValue)
                return datetimeStart.Value.ToString(dateFormatString) + " - " + datetimeEnd.Value.ToString(dateFormatString);
            else if (datetimeStart.HasValue && !datetimeEnd.HasValue)
                return datetimeStart.Value.ToString(dateFormatString);
            else if (!datetimeStart.HasValue && datetimeEnd.HasValue)
                return datetimeEnd.Value.ToString(dateFormatString);
            else
                return "";
        }


        /// <summary>
        /// Helps add a jQueryUI-based DataPicker usercontrol to a DOM-object represented by a strongly-typed Model's property.
        /// </summary>
        /// <param name="html">A strongly-typed html helper to be extended.</param>
        /// <param name="propertyExpression">A property name to add the usercontrol to.</param>
        /// <param name="dateTimeFormat">Defaults to the culture of the current thread</param>
        /// <returns>An unencoded jQueryUI javascript text.</returns>
        public static HtmlString rusisDatePickerFor<TModel, TProperty>(this HtmlHelper<TModel> html,
                        Expression<Func<TModel, TProperty>> propertyExpression,
                        DateTimeFormatInfo dateTimeFormat = null,
                        IDictionary<string, object> htmlAttributes = null)
                    where TModel : class
        {
            if (propertyExpression == null || propertyExpression.Body == null)
                throw new ArgumentNullException("propertyExpression", "The parameter (or its property 'Body') is not set");

            MemberExpression expression = propertyExpression.Body as MemberExpression;
            if (expression == null)
                throw new InvalidOperationException("The propertyExpression must represent a single property of the Model");

            string propertyName = expression.Member.Name;
            CultureInfo cultureInfo = new CultureInfo("ru-RU");
            //string dateFormatString = (dateTimeFormat ?? Thread.CurrentThread.CurrentCulture.DateTimeFormat).ShortDatePattern;
            string dateFormatString = (dateTimeFormat ?? cultureInfo.DateTimeFormat).ShortDatePattern;
            string dateFormatStringJQ = rusisDatePickerHelper.ConvertDateFormat(html, dateFormatString);

            var urlHelper = new UrlHelper(html.ViewContext.Controller.ControllerContext.RequestContext);

            var model = html.ViewData.Model;
            DateTime? value = null;
            if (model != null)
                value = (DateTime?)model.GetType().GetProperty(propertyName).GetValue(model, null);

            string htmlString =
                "<script type='text/javascript'> " +
                        " $(function () { " +
                           " $('#" + propertyName + "').datepicker({ " +
                                " dateFormat: '" + dateFormatStringJQ + "', " +
                                " showOn: 'button', " +
                                " buttonImage: '" + urlHelper.Content("~/Content/Images/calendar.gif") + "', " +
                                " buttonImageOnly: true " +
                           " }); " +
                        " }); " +
                        " </script> " +
                        "<input type='text' id='" + propertyName + "' name='" + propertyName + "' " +
                            ((value.HasValue) ? " value = '" + value.Value.ToString(dateFormatString) + "'" : "") +
                        " />";

            return new HtmlString(htmlString);
        }
    }
}
